#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <dir.h>


typedef unsigned char byte;
typedef unsigned int  word;
typedef unsigned long dwrd;

 byte far *comp;//[65535]; // compressed data buffer
 byte far *romb;//[65535]; // rom data buffer

 byte byts[0x500];         // buffer to hold completed compressed 8-Bit stream
 byte bit[8];		   // buffer for recompression bit test byte
 byte dat[40];

 byte m_10;            // Bit test byte
 byte m_11;            // loop counter
 byte m_0E;            // compressed data counter

 byte most=0;          // most compressable count
 byte tpos=0;			  // counter for compression matches
 byte cb;

 byte ab,xb,yb;        // 08-Bit A, X, and Y
 word aw,xw,yw;        // 16-Bit A, X, and Y

 word treg=0;
 word mpos=0;          // most compressable position
 word rpos=0;          // ROM position tracker
 word bpos=0;	     // temp buffer pos

 long m_0C;            // decomp'ing amount counter

 int m_08;             // position of temp buffer
 int m_04;             // storage buffer position
 int m_0A;             // back track variable

 char init=0;

 dwrd blk[0x07]={		// all text blocks I found
   0x228000,
   0x000000
 };

 dwrd ofs = 0;
 dwrd maxp;
 dwrd tmp;

 dwrd lof = 0;         // length of the file

 FILE *f1,*f2,*f3;    // File pointers

 char *ep;
 char *dstrng ="art";
 char *direct ="c:/romhack/trans/arethaII/dumps";

 char fil1[260]="c:/romhack/trans/arethaII/arethaII.smc";        // file 1 name memory
 char fil2[260]; 										      // file 2 name memory
 char block=0;	 // Index to the block you wish to decompress

void Decompress(byte);
void Recompress(byte,char);
void Dec2Bin(byte);
long Snes2Pc( long ofs );

dwrd FileLen(FILE *);
byte Instr(char *s, byte, char);

void main()
{

 clrscr();
 printf("Which do you wish to do?");
 printf("\n [1] - Decompress \n [2] - Recompress ");
 ab =0;

 while(!ab){
	ab = getch();
	switch(ab){
	 case 27  : exit(0);
	 case '1' : clrscr(); Decompress(block); break;
	 case '2' : clrscr(); Recompress(0,5); break;  // EXAMPLE: compress block 0, file 5
	 default  : ab=0; break;
	}// END SWICTH
  }// WEND
}/* End Of MAIN */



//----------------------------------------//
// The Decompression Routine for Aretha 2 //
//----------------------------------------//
void Decompress(byte dmp)
{
  FILE *fp;

  fp = fopen("c:/romhack/trans/arethaII/text.txt", "wt"); assert(fp);
  f1 = fopen(fil1, "r+b"); assert(f1);
  comp = (byte far *) malloc(65535); assert(comp);
  romb = (byte far *) malloc(65535); assert(romb);

  sprintf(fil2,"%s",direct);
  mkdir(fil2);
	memset(fil2,0,80);

  fseek(f1, Snes2Pc( blk[dmp] ), SEEK_SET);
  ab = fgetc(f1);
  cb = ((ab-0) >> 1);

  yb=0;
  while(yb < cb)
	{        		// loop through all offsets in the block
	ofs = Snes2Pc( blk[dmp] + (yb<<1) );
	fseek(f1, ofs, SEEK_SET);

	aw = getw(f1);
	ofs = ((ofs & 0xFF0000) | aw );
	memset(comp, 0, 65534);    // clean the memory

  fprintf( fp,".BLOCK %08lX\n", (blk[dmp] & 0xFF0000) | aw );


	//-------------------------------//
	// Initialize starting variables //
	//-------------------------------//
	rpos=0;
	fseek(f1, ofs + rpos, SEEK_SET);  // LDA $Rom_Address, Store in $0C:0D:0E

	aw = getw(f1);
	m_0C = (aw & 0x7FFF) + 1;
	rpos+=2;

	if ((aw & 0x8000) == 0x0000){   // if the High bit is NOT set
		m_04 = 0x1000;				// store regular data
		while(m_0C > 0){
		  comp[m_04]=fgetc(f1);
		  m_04++; m_0C--;
		}// END
		goto Done;
	}// END IF


	m_04 = 0x1000; m_08 = 0x0000;

        //-------------------------------------//
	// Start of Main Decompression Routine //
        //-------------------------------------//
	Main_852E:
	while(m_0C > 0){
	  m_10 = fgetc(f1);
	  m_11 = 0x08; rpos++;
	  Dec2Bin(m_10);

	  while(m_11 > 0){				// loop through 8 bits
		if (bit[m_11-1] == '1'){
    	          //------------------------------//
		  // Load and store Regular Data  //
                  //------------------------------//
		  _Reg_8540:
			 comp[m_04]=fgetc(f1);
			 comp[m_08]=comp[m_04];
			 m_04++; m_08++;
			 m_08 = m_08 & 0x0FFF;
			 m_0C--;
		 }else{
		  /******************************
		  ; Decompress the data         *
		  ******************************/
		  Comp_855F:
			 aw = getw(f1);
			 m_0A = (aw & 0x0FFF);
			 m_0E = ((aw >> 11) & 0x1E) + 0x03;
			 rpos+=2;

			 while(m_0E > 0){
				comp[m_04] = comp[m_0A];
				comp[m_08] = comp[m_0A];

				m_04++; m_0A++; m_08++;
				m_0A = m_0A & 0x0FFF; m_08 = m_08 & 0x0FFF;
				m_0E--; m_0C--;
				if (m_0C <= 0){ goto Done;}
			 }// WEND m_0E > 0
		 }// END IF
		m_11--;
	  }// WEND - m_11 > 0
	}// WEND m_0C > 0


  Done:
	sprintf(fil2,"%s/%s%03d.dat",direct,dstrng,yb);
//	printf("Dumped %s...\n",fil2);
	f2 = fopen(fil2, "w+b");
	fwrite(&comp[0x1000], 1, (m_04-0x1000), f2);
	fclose(f2);	yb++;
  }// WEND - yb > 0

//  fclose(f1);
  fcloseall();
	free(comp);
	free(romb);
}/* End Of Sub */



//----------------------------------------//
//                                        //
// The Recompression Routine for Aretha 2 //
//                                        //
//----------------------------------------//
void Recompress(byte dmp, char sub)
{
  if ((f1 = fopen(fil1, "r+b")) == NULL){
	  printf("%s not found.",fil1);
	  exit(0);
  }// END IF

  f2 = fopen(fil2, "r+b");
  if (!f2){
	 printf("Could not open %s.."); exit(1);
  }// END IF
  lof = FileLen(f2);

  comp = (byte far *) malloc(lof);
  if (!comp){
	 printf("Could not Allocate needed memory for COMP buffer.");
	 exit(1);
  }// END IF
  fread(comp, 1, lof, f2);   // store data to be compressed in rbuf
  fclose(f2);

  fseek(f1, blk[dmp], SEEK_SET);

//  yb=5;							  // current block to decompress
  ofs = blk[dmp] + (sub<<1);
  fseek(f1, ofs, SEEK_SET);

  aw = getw(f1);             // recompress offset
  yw = getw(f1);             // recompress offset Max / next data offset

  aw -= 0x8000;
  yw -= 0x8000;

  tmp = ofs;
  ofs = ((ofs & 0xFFF000) + aw) + 0x200;
  maxp= ((tmp & 0xFFF000) + yw) + 0x200;

  printf("%lX %lX\n", ofs,maxp);

  fseek(f1, ofs, SEEK_SET);  // LDA $Rom_Address, Store in $0C:0D:0E
  aw = getw(f1);
  tmp = ftell(f1);

  if ((aw & 0x8000) == 0){
	 printf("Routine not supported...");
	 exit(0);
  }// END IF

  rpos=0; m_04=0;

  /**************************************
  ; Start of Main Recompression Routine *
  **************************************/
  Re_Comp_Main:

  while(m_04 < lof){			// loop through all of the data
	 m_11=0x08; treg=1;

	 while(m_11 > 0){       // loop through 8 bits
		if (m_04 > 0xFFF){
		 m_0A = m_04 - 0xFFF;
		}else{
		 m_0A = 0;
		}// END IF
		most=0;

		while(m_0A < m_04){
		  if (comp[m_04] == comp[m_0A]){ tpos=0;
			 while(comp[m_04+tpos] == comp[m_0A+tpos] && (m_04+tpos) < lof && tpos < 0x21){
				tpos++;
			 }// WEND

			 if (tpos > most){				// if more data found, change most
				mpos = (m_0A & 0xFFF); most=tpos;
			 }// END IF

			 if (most >= 33){ most = 33; break;}
		  }// END IF
		 m_0A++;
		}// WEND - m_0A < cpos


		/* Test for Regular or Compress data storage */
		_Comp_Test_:
		if (most >= 3){
		  aw = (((most-3 & 0x1E)) << 11) + (mpos & 0x0FFF);
		  byts[treg]=(aw & 0xFF); treg++;
		  byts[treg]=(aw >> 8); treg++;
		  bit[m_11-1]='0'; m_04 += ((most-3) & 0x1E) + 3;
		}else{       	// store the regular data
		  byts[treg] = comp[m_04]; treg++;
		  m_04++; bit[m_11-1]='1';
		}// END IF
		m_11--;
	 }// WEND - m_01 < 8
	 bit[8]='\0';

	 ab = strtol(bit, &ep, 2);
	 byts[0]=ab;

	 yw=0;
	 while(yw < treg){
	  romb[rpos] = byts[yw];
	  rpos++; yw++;
	 }// WEND
  }// WEND


  fseek(f1, tmp, SEEK_SET);  // LDA $Rom_Address, Store in $0C:0D:0E

  m_0A=0;                    // store as many bytes as you are allowed
  while(m_0A < rpos && (ftell(f1)+1) < maxp){
	ab = romb[m_0A];
	fputc(ab, f1); m_0A++;
  }// END IF

  if (m_0A >= rpos){
	 printf(
	  "Error, tried to compress more data than was available..\n"
	  "Amount of bytes over: %lX",rpos-m_0A
	 );
  }// END IF
  fclose(f1); fclose(f2);
}/* End Of SUB */




/************************
; Finds char in string  *
************************/
byte Instr(char *s, byte chr, char way)
{
 if (!way){
	xb = 0;
	while(s[xb] != chr && xb < strlen(s)){
	 xb++;
	}// WEND
 }else{
	xb = strlen(s);
	while(s[xb] != chr && xb > 0){
	 xb--;
	}// WEND
 }// END IF
 return xb;
}/* END OF SUB */


/***********************
; Convert to Binary    *
***********************/
void Dec2Bin(byte byt)
{
 byte dec = byt;
 byte c = 0;

 while(c < 8){
  bit[c] = ((dec & 0x80) >> 7) + '0';
  dec <<= 1; c++;
 }// WEND
 bit[c]='\0';
}/* End Of SUB */


/*********************
; Get length of file *
*********************/
dwrd FileLen(FILE *tf)
{
 dwrd tlof;

 fseek(tf,0,SEEK_END);
 tlof = ftell(tf);
 fseek(tf,0,SEEK_SET);
 return tlof;
}// END OF SUB


//-----------------------------//
// Load the program data Bank  //
//-----------------------------//
long Snes2Pc( long ofs )
{
  LONG val;

  if ( !ofs ) return 0;

  val = (((ofs | 0x800000) - 0x800000) & 0xFF0000) >> 1;
  val+= ((ofs & 0xFFFF) - 0x8000);

  return val;
}// END OF SUB
